#include <iostream>
#include <unistd.h>
#include <assert.h>
#include <string>
#include <cstdio>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

using namespace std;
//管道:让不同的进程看到 同一份资源
//特点: 1.管道是用来具有血缘关系的进程进行进程间通信 常用于父子进程
//      2.管道具有访问控制，利于多进程之间的协同 即当管道文件被写满时,必须要当管道文件被读取后,才能再写入,同样的,当管道文件中没有数据时,要等到写入管道文件后,才能被读取
//      3.管道提供的式面向流式的通信服务
//      4.管道是基于文件的,文件的生命周期是随进程的,所以管道文件的生命周期也是随进程的
//      5.管道是单向通信的 （半双工通信的一种特殊情况）

//a.写快，读慢，写满了后就不能再写了
//b.写慢，读快，管道文件没有数据时，读必须等待
//c.写关闭，读到0，表示读到管道文件结尾
//d.读关闭，写不关闭，OS会终止写进程

//int pipe(int pipefd[2]) pipefd数组是输出型参数，存放被打开管道文件的fd
//成功返回0,失败返回-1
int main()
{
    //1.创建管道
    int pipefd[2] = {0};//pipefd[0]:读端 pipefd[1]:写端
    int n = pipe(pipefd);
    assert(n != -1);
    (void)n;

#ifdef DEBUG
    cout << "pipedf[0]: " << pipefd[0] << endl;
    cout << "pipedf[1]: " << pipefd[1] << endl;
#endif

    //2.创建子进程
    pid_t id = fork();
    assert(id != -1);

    if(id == 0)
    {
        //子进程
        //3.构建单向通信的管道
        //close(pipefd[0]);//子进程读端关闭，父进程写端没有关闭，OS自动终止写
        close(pipefd[1]);//关闭子进程的管道文件写端
        char buff[1024];
        while(true)
        {
            //如果写入的一方,fd没有关闭,对于读取的一方,如果管道文件有数据,就读,没有数据就等
            //如果写入的一方,fd关闭,对于读取的一方,read会返回0,表示读到了管道文件的结尾
            ssize_t s = read(pipefd[0],buff,sizeof(buff) - 1);
            if(s > 0) 
            {
                buff[s] = '\0';
                cout << "chid get a message "<< getpid() <<  " Father# " << buff << endl;
            }
            else if(s == 0)
            {
                cout << "writer quit(father), me quit! " << endl;
                break;
            }
        }
        //close(pipefd[0]);
        exit(0);
    }

    //父进程
    //3.构建单向通信的管道,关闭父进程不需要的fd
    close(pipefd[0]);//关闭父进程的管道文件读端
    string message = "我是父进程,我正在给你发消息";
    char send_buff[1024];
    int count = 0;
    while(true)
    {
        //构建一个变化的字符串
        snprintf(send_buff,sizeof(send_buff),"%s[%d] : %d",message.c_str(),getpid(),count);
        count++;
        write(pipefd[1],send_buff,strlen(send_buff));
        sleep(1);
    }

    pid_t ret = waitpid(id,nullptr,0);//阻塞式等待
    assert(ret != -1);
    (void)ret;

    close(pipefd[1]);
    return 0;
}
